Guest User

Untitled

a guest
Nov 16th, 2018
128
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 18.94 KB | None | 0 0
  1. package org.opencv.android;
  2.  
  3. import java.util.List;
  4.  
  5. import org.opencv.R;
  6. import org.opencv.core.Mat;
  7. import org.opencv.core.Size;
  8.  
  9. import android.app.Activity;
  10. import android.app.AlertDialog;
  11. import android.content.Context;
  12. import android.content.DialogInterface;
  13. import android.content.res.TypedArray;
  14. import android.graphics.Bitmap;
  15. import android.graphics.Canvas;
  16. import android.graphics.Matrix;
  17. import android.graphics.Rect;
  18. import android.hardware.Camera;
  19. import android.util.AttributeSet;
  20. import android.util.Log;
  21. import android.view.SurfaceHolder;
  22. import android.view.SurfaceView;
  23.  
  24. /**
  25. * This is a basic class, implementing the interaction with Camera and OpenCV library.
  26. * The main responsibility of it - is to control when camera can be enabled, process the frame,
  27. * call external listener to make any adjustments to the frame and then draw the resulting
  28. * frame to the screen.
  29. * The clients shall implement CvCameraViewListener.
  30. */
  31. public abstract class CameraBridgeViewBase extends SurfaceView implements SurfaceHolder.Callback {
  32.  
  33. private static final String TAG = "CameraBridge";
  34. private static final int MAX_UNSPECIFIED = -1;
  35. private static final int STOPPED = 0;
  36. private static final int STARTED = 1;
  37.  
  38. private int mState = STOPPED;
  39. private Bitmap mCacheBitmap;
  40. private CvCameraViewListener2 mListener;
  41. private boolean mSurfaceExist;
  42. private Object mSyncObject = new Object();
  43.  
  44. protected int mFrameWidth;
  45. protected int mFrameHeight;
  46. protected int mMaxHeight;
  47. protected int mMaxWidth;
  48. protected float mScale = 0;
  49. protected int mPreviewFormat = RGBA;
  50. protected int mCameraIndex = CAMERA_ID_ANY;
  51. protected boolean mEnabled;
  52. protected FpsMeter mFpsMeter = null;
  53. private final Matrix mMatrix = new Matrix();
  54.  
  55. public static final int CAMERA_ID_ANY = -1;
  56. public static final int CAMERA_ID_BACK = 99;
  57. public static final int CAMERA_ID_FRONT = 98;
  58. public static final int RGBA = 1;
  59. public static final int GRAY = 2;
  60.  
  61. public CameraBridgeViewBase(Context context, int cameraId) {
  62. super(context);
  63. mCameraIndex = cameraId;
  64. getHolder().addCallback(this);
  65. mMaxWidth = MAX_UNSPECIFIED;
  66. mMaxHeight = MAX_UNSPECIFIED;
  67. }
  68.  
  69. public CameraBridgeViewBase(Context context, AttributeSet attrs) {
  70. super(context, attrs);
  71.  
  72. int count = attrs.getAttributeCount();
  73. Log.d(TAG, "Attr count: " + Integer.valueOf(count));
  74.  
  75. TypedArray styledAttrs = getContext().obtainStyledAttributes(attrs, R.styleable.CameraBridgeViewBase);
  76. if (styledAttrs.getBoolean(R.styleable.CameraBridgeViewBase_show_fps, false))
  77. enableFpsMeter();
  78.  
  79. mCameraIndex = styledAttrs.getInt(R.styleable.CameraBridgeViewBase_camera_id, -1);
  80.  
  81. getHolder().addCallback(this);
  82. mMaxWidth = MAX_UNSPECIFIED;
  83. mMaxHeight = MAX_UNSPECIFIED;
  84. styledAttrs.recycle();
  85. }
  86.  
  87. /**
  88. * Sets the camera index
  89. * @param cameraIndex new camera index
  90. */
  91. public void setCameraIndex(int cameraIndex) {
  92. this.mCameraIndex = cameraIndex;
  93. }
  94.  
  95. public interface CvCameraViewListener {
  96. /**
  97. * This method is invoked when camera preview has started. After this method is invoked
  98. * the frames will start to be delivered to client via the onCameraFrame() callback.
  99. * @param width - the width of the frames that will be delivered
  100. * @param height - the height of the frames that will be delivered
  101. */
  102. public void onCameraViewStarted(int width, int height);
  103.  
  104. /**
  105. * This method is invoked when camera preview has been stopped for some reason.
  106. * No frames will be delivered via onCameraFrame() callback after this method is called.
  107. */
  108. public void onCameraViewStopped();
  109.  
  110. /**
  111. * This method is invoked when delivery of the frame needs to be done.
  112. * The returned values - is a modified frame which needs to be displayed on the screen.
  113. * TODO: pass the parameters specifying the format of the frame (BPP, YUV or RGB and etc)
  114. */
  115. public Mat onCameraFrame(Mat inputFrame);
  116. }
  117.  
  118. public interface CvCameraViewListener2 {
  119. /**
  120. * This method is invoked when camera preview has started. After this method is invoked
  121. * the frames will start to be delivered to client via the onCameraFrame() callback.
  122. * @param width - the width of the frames that will be delivered
  123. * @param height - the height of the frames that will be delivered
  124. */
  125. public void onCameraViewStarted(int width, int height);
  126.  
  127. /**
  128. * This method is invoked when camera preview has been stopped for some reason.
  129. * No frames will be delivered via onCameraFrame() callback after this method is called.
  130. */
  131. public void onCameraViewStopped();
  132.  
  133. /**
  134. * This method is invoked when delivery of the frame needs to be done.
  135. * The returned values - is a modified frame which needs to be displayed on the screen.
  136. * TODO: pass the parameters specifying the format of the frame (BPP, YUV or RGB and etc)
  137. */
  138. public Mat onCameraFrame(CvCameraViewFrame inputFrame);
  139. };
  140.  
  141. protected class CvCameraViewListenerAdapter implements CvCameraViewListener2 {
  142. public CvCameraViewListenerAdapter(CvCameraViewListener oldStypeListener) {
  143. mOldStyleListener = oldStypeListener;
  144. }
  145.  
  146. public void onCameraViewStarted(int width, int height) {
  147. mOldStyleListener.onCameraViewStarted(width, height);
  148. }
  149.  
  150. public void onCameraViewStopped() {
  151. mOldStyleListener.onCameraViewStopped();
  152. }
  153.  
  154. public Mat onCameraFrame(CvCameraViewFrame inputFrame) {
  155. Mat result = null;
  156. switch (mPreviewFormat) {
  157. case RGBA:
  158. result = mOldStyleListener.onCameraFrame(inputFrame.rgba());
  159. break;
  160. case GRAY:
  161. result = mOldStyleListener.onCameraFrame(inputFrame.gray());
  162. break;
  163. default:
  164. Log.e(TAG, "Invalid frame format! Only RGBA and Gray Scale are supported!");
  165. };
  166.  
  167. return result;
  168. }
  169.  
  170. public void setFrameFormat(int format) {
  171. mPreviewFormat = format;
  172. }
  173.  
  174. private int mPreviewFormat = RGBA;
  175. private CvCameraViewListener mOldStyleListener;
  176. };
  177.  
  178. /**
  179. * This class interface is abstract representation of single frame from camera for onCameraFrame callback
  180. * Attention: Do not use objects, that represents this interface out of onCameraFrame callback!
  181. */
  182. public interface CvCameraViewFrame {
  183.  
  184. /**
  185. * This method returns RGBA Mat with frame
  186. */
  187. public Mat rgba();
  188.  
  189. /**
  190. * This method returns single channel gray scale Mat with frame
  191. */
  192. public Mat gray();
  193. };
  194.  
  195. public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) {
  196. Log.d(TAG, "call surfaceChanged event");
  197. synchronized(mSyncObject) {
  198. if (!mSurfaceExist) {
  199. mSurfaceExist = true;
  200. checkCurrentState();
  201. } else {
  202. /** Surface changed. We need to stop camera and restart with new parameters */
  203. /* Pretend that old surface has been destroyed */
  204. mSurfaceExist = false;
  205. checkCurrentState();
  206. /* Now use new surface. Say we have it now */
  207. mSurfaceExist = true;
  208. checkCurrentState();
  209. }
  210. }
  211. }
  212.  
  213. public void surfaceCreated(SurfaceHolder holder) {
  214. /* Do nothing. Wait until surfaceChanged delivered */
  215. }
  216.  
  217. public void surfaceDestroyed(SurfaceHolder holder) {
  218. synchronized(mSyncObject) {
  219. mSurfaceExist = false;
  220. checkCurrentState();
  221. }
  222. }
  223.  
  224. /**
  225. * This method is provided for clients, so they can enable the camera connection.
  226. * The actual onCameraViewStarted callback will be delivered only after both this method is called and surface is available
  227. */
  228. public void enableView() {
  229. synchronized(mSyncObject) {
  230. mEnabled = true;
  231. checkCurrentState();
  232. }
  233. }
  234.  
  235. /**
  236. * This method is provided for clients, so they can disable camera connection and stop
  237. * the delivery of frames even though the surface view itself is not destroyed and still stays on the scren
  238. */
  239. public void disableView() {
  240. synchronized(mSyncObject) {
  241. mEnabled = false;
  242. checkCurrentState();
  243. }
  244. }
  245.  
  246. /**
  247. * This method enables label with fps value on the screen
  248. */
  249. public void enableFpsMeter() {
  250. if (mFpsMeter == null) {
  251. mFpsMeter = new FpsMeter();
  252. mFpsMeter.setResolution(mFrameWidth, mFrameHeight);
  253. }
  254. }
  255.  
  256. public void disableFpsMeter() {
  257. mFpsMeter = null;
  258. }
  259.  
  260. /**
  261. *
  262. * @param listener
  263. */
  264.  
  265. public void setCvCameraViewListener(CvCameraViewListener2 listener) {
  266. mListener = listener;
  267. }
  268.  
  269. public void setCvCameraViewListener(CvCameraViewListener listener) {
  270. CvCameraViewListenerAdapter adapter = new CvCameraViewListenerAdapter(listener);
  271. adapter.setFrameFormat(mPreviewFormat);
  272. mListener = adapter;
  273. }
  274.  
  275. /**
  276. * This method sets the maximum size that camera frame is allowed to be. When selecting
  277. * size - the biggest size which less or equal the size set will be selected.
  278. * As an example - we set setMaxFrameSize(200,200) and we have 176x152 and 320x240 sizes. The
  279. * preview frame will be selected with 176x152 size.
  280. * This method is useful when need to restrict the size of preview frame for some reason (for example for video recording)
  281. * @param maxWidth - the maximum width allowed for camera frame.
  282. * @param maxHeight - the maximum height allowed for camera frame
  283. */
  284. public void setMaxFrameSize(int maxWidth, int maxHeight) {
  285. mMaxWidth = maxWidth;
  286. mMaxHeight = maxHeight;
  287. }
  288.  
  289. public void SetCaptureFormat(int format)
  290. {
  291. mPreviewFormat = format;
  292. if (mListener instanceof CvCameraViewListenerAdapter) {
  293. CvCameraViewListenerAdapter adapter = (CvCameraViewListenerAdapter) mListener;
  294. adapter.setFrameFormat(mPreviewFormat);
  295. }
  296. }
  297.  
  298. /**
  299. * Called when mSyncObject lock is held
  300. */
  301. private void checkCurrentState() {
  302. Log.d(TAG, "call checkCurrentState");
  303. int targetState;
  304.  
  305. if (mEnabled && mSurfaceExist && getVisibility() == VISIBLE) {
  306. targetState = STARTED;
  307. } else {
  308. targetState = STOPPED;
  309. }
  310.  
  311. if (targetState != mState) {
  312. /* The state change detected. Need to exit the current state and enter target state */
  313. processExitState(mState);
  314. mState = targetState;
  315. processEnterState(mState);
  316. }
  317. }
  318.  
  319. private void processEnterState(int state) {
  320. Log.d(TAG, "call processEnterState: " + state);
  321. switch(state) {
  322. case STARTED:
  323. onEnterStartedState();
  324. if (mListener != null) {
  325. mListener.onCameraViewStarted(mFrameWidth, mFrameHeight);
  326. }
  327. break;
  328. case STOPPED:
  329. onEnterStoppedState();
  330. if (mListener != null) {
  331. mListener.onCameraViewStopped();
  332. }
  333. break;
  334. };
  335. }
  336.  
  337. private void processExitState(int state) {
  338. Log.d(TAG, "call processExitState: " + state);
  339. switch(state) {
  340. case STARTED:
  341. onExitStartedState();
  342. break;
  343. case STOPPED:
  344. onExitStoppedState();
  345. break;
  346. };
  347. }
  348.  
  349. private void onEnterStoppedState() {
  350. /* nothing to do */
  351. }
  352.  
  353. private void onExitStoppedState() {
  354. /* nothing to do */
  355. }
  356.  
  357. // NOTE: The order of bitmap constructor and camera connection is important for android 4.1.x
  358. // Bitmap must be constructed before surface
  359. private void onEnterStartedState() {
  360. Log.d(TAG, "call onEnterStartedState");
  361. /* Connect camera */
  362. if (!connectCamera(getWidth(), getHeight())) {
  363. AlertDialog ad = new AlertDialog.Builder(getContext()).create();
  364. ad.setCancelable(false); // This blocks the 'BACK' button
  365. ad.setMessage("It seems that you device does not support camera (or it is locked). Application will be closed.");
  366. ad.setButton(DialogInterface.BUTTON_NEUTRAL, "OK", new DialogInterface.OnClickListener() {
  367. public void onClick(DialogInterface dialog, int which) {
  368. dialog.dismiss();
  369. ((Activity) getContext()).finish();
  370. }
  371. });
  372. ad.show();
  373.  
  374. }
  375. }
  376.  
  377. private void onExitStartedState() {
  378. disconnectCamera();
  379. if (mCacheBitmap != null) {
  380. mCacheBitmap.recycle();
  381. }
  382. }
  383.  
  384. /**
  385. * This method shall be called by the subclasses when they have valid
  386. * object and want it to be delivered to external client (via callback) and
  387. * then displayed on the screen.
  388. * @param frame - the current frame to be delivered
  389. */
  390. protected void deliverAndDrawFrame(CvCameraViewFrame frame) {
  391. Mat modified;
  392.  
  393. if (mListener != null) {
  394. modified = mListener.onCameraFrame(frame);
  395. } else {
  396. modified = frame.rgba();
  397. }
  398.  
  399. boolean bmpValid = true;
  400. if (modified != null) {
  401. try {
  402. Utils.matToBitmap(modified, mCacheBitmap);
  403. } catch(Exception e) {
  404. Log.e(TAG, "Mat type: " + modified);
  405. Log.e(TAG, "Bitmap type: " + mCacheBitmap.getWidth() + "*" + mCacheBitmap.getHeight());
  406. Log.e(TAG, "Utils.matToBitmap() throws an exception: " + e.getMessage());
  407. bmpValid = false;
  408. }
  409. }
  410.  
  411. if (bmpValid && mCacheBitmap != null) {
  412. Canvas canvas = getHolder().lockCanvas();
  413. if (canvas != null) {
  414. int saveCount = canvas.save();
  415. canvas.setMatrix(mMatrix);
  416.  
  417. mScale = Math.max((float) canvas.getHeight() / mCacheBitmap.getWidth(), (float) canvas.getWidth() / mCacheBitmap.getHeight());
  418.  
  419.  
  420. canvas.drawColor(0, android.graphics.PorterDuff.Mode.CLEAR);
  421. Log.d(TAG, "mStretch value: " + mScale);
  422.  
  423. if (mScale != 0) {
  424. canvas.drawBitmap(mCacheBitmap, new Rect(0,0,mCacheBitmap.getWidth(), mCacheBitmap.getHeight()),
  425. new Rect((int)((canvas.getWidth() - mScale*mCacheBitmap.getWidth()) / 2),
  426. (int)((canvas.getHeight() - mScale*mCacheBitmap.getHeight()) / 2),
  427. (int)((canvas.getWidth() - mScale*mCacheBitmap.getWidth()) / 2 + mScale*mCacheBitmap.getWidth()),
  428. (int)((canvas.getHeight() - mScale*mCacheBitmap.getHeight()) / 2 + mScale*mCacheBitmap.getHeight())), null);
  429. } else {
  430. canvas.drawBitmap(mCacheBitmap, new Rect(0,0,mCacheBitmap.getWidth(), mCacheBitmap.getHeight()),
  431. new Rect((canvas.getWidth() - mCacheBitmap.getWidth()) / 2,
  432. (canvas.getHeight() - mCacheBitmap.getHeight()) / 2,
  433. (canvas.getWidth() - mCacheBitmap.getWidth()) / 2 + mCacheBitmap.getWidth(),
  434. (canvas.getHeight() - mCacheBitmap.getHeight()) / 2 + mCacheBitmap.getHeight()), null);
  435. }
  436. canvas.restoreToCount(saveCount);
  437.  
  438. if (mFpsMeter != null) {
  439. mFpsMeter.measure();
  440. mFpsMeter.draw(canvas, 20, 30);
  441. }
  442. getHolder().unlockCanvasAndPost(canvas);
  443. }
  444. }
  445. }
  446.  
  447. /**
  448. * This method is invoked shall perform concrete operation to initialize the camera.
  449. * CONTRACT: as a result of this method variables mFrameWidth and mFrameHeight MUST be
  450. * initialized with the size of the Camera frames that will be delivered to external processor.
  451. * @param width - the width of this SurfaceView
  452. * @param height - the height of this SurfaceView
  453. */
  454. protected abstract boolean connectCamera(int width, int height);
  455.  
  456. /**
  457. * Disconnects and release the particular camera object being connected to this surface view.
  458. * Called when syncObject lock is held
  459. */
  460. protected abstract void disconnectCamera();
  461.  
  462. // NOTE: On Android 4.1.x the function must be called before SurfaceTexture constructor!
  463. protected void AllocateCache()
  464. {
  465. mCacheBitmap = Bitmap.createBitmap(mFrameWidth, mFrameHeight, Bitmap.Config.ARGB_8888);
  466. }
  467.  
  468. public interface ListItemAccessor {
  469. public int getWidth(Object obj);
  470. public int getHeight(Object obj);
  471. };
  472.  
  473. /**
  474. * This helper method can be called by subclasses to select camera preview size.
  475. * It goes over the list of the supported preview sizes and selects the maximum one which
  476. * fits both values set via setMaxFrameSize() and surface frame allocated for this view
  477. * @param supportedSizes
  478. * @param surfaceWidth
  479. * @param surfaceHeight
  480. * @return optimal frame size
  481. */
  482. protected Size calculateCameraFrameSize(List<?> supportedSizes, ListItemAccessor accessor, int surfaceWidth, int surfaceHeight) {
  483. int calcWidth = 0;
  484. int calcHeight = 0;
  485.  
  486. int maxAllowedWidth = (mMaxWidth != MAX_UNSPECIFIED && mMaxWidth < surfaceWidth)? mMaxWidth : surfaceWidth;
  487. int maxAllowedHeight = (mMaxHeight != MAX_UNSPECIFIED && mMaxHeight < surfaceHeight)? mMaxHeight : surfaceHeight;
  488.  
  489. for (Object size : supportedSizes) {
  490. int width = accessor.getWidth(size);
  491. int height = accessor.getHeight(size);
  492.  
  493. if (width <= maxAllowedWidth && height <= maxAllowedHeight) {
  494. if (width >= calcWidth && height >= calcHeight) {
  495. calcWidth = (int) width;
  496. calcHeight = (int) height;
  497. }
  498. }
  499. }
  500.  
  501. return new Size(calcWidth, calcHeight);
  502. }
  503.  
  504. @Override
  505. public void layout(int l, int t, int r, int b) {
  506. super.layout(l, t, r, b);
  507. updateMatrix();
  508. }
  509.  
  510. @Override
  511. protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  512. super.onMeasure(widthMeasureSpec, heightMeasureSpec);
  513. updateMatrix();
  514. }
  515.  
  516. private void updateMatrix() {
  517. float hw = this.getWidth() / 2.0f;
  518. float hh = this.getHeight() / 2.0f;
  519. boolean isFrontCamera = Camera.CameraInfo.CAMERA_FACING_FRONT == mCameraIndex;
  520. mMatrix.reset();
  521. if (isFrontCamera) {
  522. mMatrix.preScale(-1, 1, hw, hh);
  523. }
  524. mMatrix.preTranslate(hw, hh);
  525. if (isFrontCamera)
  526. mMatrix.preRotate(270);
  527. else
  528. mMatrix.preRotate(90);
  529. mMatrix.preTranslate(-hw, -hh);
  530. }
  531. }
Add Comment
Please, Sign In to add comment