-
Notifications
You must be signed in to change notification settings - Fork 2
/
MainActivity.java
265 lines (229 loc) · 8.69 KB
/
MainActivity.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
package boofcv.androidautofocus;
import android.Manifest;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.pm.PackageManager;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Typeface;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CaptureRequest;
import android.os.Bundle;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.view.SurfaceView;
import android.view.TextureView;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.Toast;
import java.util.Locale;
import boofcv.alg.feature.detect.edge.GGradientToEdgeFeatures;
import boofcv.alg.filter.derivative.DerivativeType;
import boofcv.alg.filter.derivative.GImageDerivativeOps;
import boofcv.alg.misc.ImageStatistics;
import boofcv.android.camera2.VisualizeCamera2Activity;
import boofcv.core.image.border.BorderType;
import boofcv.struct.image.GrayF32;
import boofcv.struct.image.GrayS16;
import boofcv.struct.image.GrayU8;
import boofcv.struct.image.ImageBase;
import boofcv.struct.image.ImageType;
/**
* Example of how to control the camera with manual focus to select the optimal focus for viewing.
*
* @author Peter Abeles
*/
public class MainActivity extends VisualizeCamera2Activity
implements View.OnClickListener
{
public static final String TAG = "FocusActivity";
// how long it lets the camera wait before trying another focus value
public static final long FOCUS_PERIOD = 50; // milliseconds
// number if discrete values it will try when focusing
public static final int FOCUS_LEVELS = 50;
// Specifies the current state in the auto focus routine that it's in
State state = State.INITIALIZE;
// The current focus value being considered
int focusIndex = 0;
// The focus index with the best edge value
int focusBestIndex = 0;
// The value of the best index
float focusBestValue = 0;
// How long until it can attempt to focus again
long focusTime;
// Most recently computed edge value
float edgeValue;
//-------- Workspace for image processing.
// the image gradient
GrayS16 derivX = new GrayS16(1,1);
GrayS16 derivY = new GrayS16(1,1);
// Gradient intensity
GrayF32 intensity = new GrayF32(1,1);
// Specify paints for rending the GUI
private Paint paintText = new Paint();
private Paint paintGray = new Paint();
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
TextureView view = findViewById(R.id.camera_view);
FrameLayout surface = findViewById(R.id.camera_frame);
surface.setOnClickListener(this);
// You need explicit permission from the user to access the camera
requestCameraPermission();
// Specify what format you want the image in for image processing
// The line below specifies an 8-bit gray scale image
setImageType(ImageType.single(GrayU8.class));
// Tell the library what resolution you want. It searches for the resolution
// which has the total number of pixels closest to this value. Most computer vision
// algorithms run much faster and even work better at low resolutions.
targetResolution = 640*480;
// If this is too simplistic for you, feel free to override #selectResolution()
// We want to display the raw camera feed so we will turn off rendering
// converted images
bitmapMode = BitmapMode.NONE;
// Configure text for display and adjust the size intelligently
paintText.setStrokeWidth(4*displayMetrics.density);
paintText.setTextSize(14*displayMetrics.density);
paintText.setTextAlign(Paint.Align.LEFT);
paintText.setARGB(0xFF,0xFF,0xB0,0);
paintText.setTypeface(Typeface.create(Typeface.MONOSPACE, Typeface.BOLD));
paintGray.setARGB(0xA0,0,0,0);
startCamera(surface,view);
}
@Override
protected void processImage(ImageBase image) {
// We specified earlier to give us an 8-bit gray scale image
GrayU8 gray = (GrayU8)image;
// Ensure that work space images are the appropriate size
derivX.reshape(gray.width,gray.height);
derivY.reshape(gray.width,gray.height);
intensity.reshape(gray.width,gray.height);
// Compute the gradient and Euclidean edge intensity
GImageDerivativeOps.gradient(DerivativeType.SOBEL,gray,derivX,derivY, BorderType.EXTENDED);
GGradientToEdgeFeatures.intensityE(derivX, derivY, intensity);
// Find the average edge value
edgeValue = ImageStatistics.mean(intensity);
}
@Override
protected void onDrawFrame(SurfaceView view, Canvas canvas) {
super.onDrawFrame(view, canvas);
// Darken the image behind the text to make it easier to read
canvas.drawRect(0,100,view.getRight(),220,paintGray);
// Render text information on top of the camera preview
// this shows the current edge value and what the app is doing
canvas.drawText(String.format(Locale.getDefault(),
"State %s", state.toString()),0,140, paintText);
int focus = state == State.FIXED ? focusBestIndex : focusIndex;
canvas.drawText(String.format(Locale.getDefault(),
"Focus %4d Edge %5.1f", focus, edgeValue),
0, 200, paintText);
switch( state ) {
case FOCUSING:
// If enough time has elapsed that the camera should have changed focus and
// the camera is ready to have its configurations changed, save the edge value
// and tell it update the camera settings
if( focusTime < System.currentTimeMillis() && isCameraReadyReconfiguration() ) {
if (edgeValue > focusBestValue) {
focusBestValue = edgeValue;
focusBestIndex = focusIndex;
}
state = State.PENDING;
// Tell the camera to change its settings. We know that it will accept
// new settings since we checked isCameraReadyReconfiguration() otherwise
// this coulf fail and return false
changeCameraConfiguration();
}
break;
}
}
@Override
protected void configureCamera(CameraDevice device, CameraCharacteristics characteristics, CaptureRequest.Builder captureRequestBuilder) {
// set focus control to manual
captureRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_OFF);
// get a list of acceptable values
Float minFocus = characteristics.get(CameraCharacteristics.LENS_INFO_MINIMUM_FOCUS_DISTANCE);
switch( state ) {
case INITIALIZE:{
if( minFocus == null ) {
Toast.makeText(this,"manual focus not supported", Toast.LENGTH_SHORT).show();
state = State.UNSUPPORTED;
} else {
focusBestIndex = 0;
focusBestValue = 0;
focusIndex = 0;
focusTime = System.currentTimeMillis()+FOCUS_PERIOD;
state = State.FOCUSING;
captureRequestBuilder.set(CaptureRequest.LENS_FOCUS_DISTANCE, 0f);
}
}break;
case PENDING:{
focusIndex++;
if( focusIndex < FOCUS_LEVELS ) {
focusTime = System.currentTimeMillis()+FOCUS_PERIOD;
captureRequestBuilder.set(CaptureRequest.LENS_FOCUS_DISTANCE, minFocus*focusIndex/(FOCUS_LEVELS-1));
state = State.FOCUSING;
} else {
captureRequestBuilder.set(CaptureRequest.LENS_FOCUS_DISTANCE, minFocus*focusBestIndex/(FOCUS_LEVELS-1));
state = State.FIXED;
}
}break;
}
}
/**
* Newer versions of Android require explicit permission from the user
*/
private void requestCameraPermission() {
int permissionCheck = ContextCompat.checkSelfPermission(this,
Manifest.permission.CAMERA);
if( permissionCheck != android.content.pm.PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.CAMERA},
0);
// a dialog should open and this dialog will resume when a decision has been made
}
}
@Override
public void onClick(View v) {
// If the user touches the screen and it has already finished focusing, start again
if( state == State.FIXED ) {
state = State.INITIALIZE;
changeCameraConfiguration();
}
}
@Override
public void onRequestPermissionsResult(int requestCode,
String permissions[], int[] grantResults) {
switch (requestCode) {
case 0: {
// If request is cancelled, the result arrays are empty.
if (!(grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED)) {
dialogNoCameraPermission();
}
}
}
}
private void dialogNoCameraPermission() {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setMessage("Denied access to the camera! Exiting.")
.setCancelable(false)
.setPositiveButton("OK", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
System.exit(0);
}
});
AlertDialog alert = builder.create();
alert.show();
}
/**
* State in automatic focus
*/
enum State {
INITIALIZE,
UNSUPPORTED,
FOCUSING,
PENDING,
FIXED
}
}