Advertisement
Guest User

Untitled

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